/**
  ******************************************************************************
  * @file    bsp_led.c
  * @author  Iron
  * @date    2018-08-03
  * @version v1.0
  * @brief   hal led c file
  */

/** @addtogroup GROUP_BSP
  * @{
  */

/* includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "bsp_led.h"

/* private typedef -----------------------------------------------------------*/
/* private define ------------------------------------------------------------*/
#define LED_NUM                 4

#define LED_STATE_OFF           GPIO_PIN_SET
#define LED_STATE_ON            GPIO_PIN_RESET

#define LED1_PORT               GPIOA
#define LED1_PIN                GPIO_PIN_8
#define LED2_PORT               GPIOB
#define LED2_PIN                GPIO_PIN_3
#define LED3_PORT               GPIOB
#define LED3_PIN                GPIO_PIN_4
#define LED4_PORT               GPIOB
#define LED4_PIN                GPIO_PIN_5

#define LED_Set(id, state)  HAL_GPIO_WritePin(led_gpio[id].port, led_gpio[id].pin, state)
#define LED_Get(id)                 HAL_GPIO_ReadPin(led_gpio[id].port, led_gpio[id].pin)
#define LED_Toggle(id)          HAL_GPIO_TogglePin(led_gpio[id].port, led_gpio[id].pin)

#define LED_PortInit()

/* private macro -------------------------------------------------------------*/
/* private variables ---------------------------------------------------------*/
static const struct
{
    GPIO_TypeDef *port;
    uint16_t pin;
} led_gpio[LED_NUM] =
{
    {LED1_PORT, LED1_PIN},
    {LED2_PORT, LED2_PIN},
    {LED3_PORT, LED3_PIN},
    {LED4_PORT, LED4_PIN},
};

static struct _led_timer
{
    GPIO_PinState state;
    uint32_t numBlinks;
    uint32_t percent;
    uint32_t percent_max;
    uint32_t percent_count;
    /* RTOS Timer */
    const char *tmrName;
    TimerHandle_t tmrId;
    StaticTimer_t tmr;
} led_timer[LED_NUM];

static const char *LedTmrNames[LED_NUM] =
{
    "tmr-led1",
    "tmr-led2",
    "tmr-led3",
    "tmr-led4",
};

/* private function prototypes -----------------------------------------------*/
/* private functions ---------------------------------------------------------*/
extern void _Error_Handler(char *file, int line);

static void led_timer_cb(TimerHandle_t xExpiredTimer)
{
    int32_t id = (int32_t)pvTimerGetTimerID(xExpiredTimer);

    if (id < LED_NUM)
    {
        if (led_timer[id].numBlinks == 0)
        {
            xTimerStop(led_timer[id].tmrId, 0);
            LED_Set(id, led_timer[id].state);
        }
        else
        {
            led_timer[id].percent_count++;
            if (led_timer[id].percent_count >= led_timer[id].percent_max)
            {
                led_timer[id].numBlinks--;
                led_timer[id].percent_count = 0;
                LED_Set(id, LED_STATE_ON);
            }
            if (led_timer[id].percent_count == led_timer[id].percent)
            {
                LED_Set(id, LED_STATE_OFF);
            }
        }
    }
}

/**
  * @brief  This LED initialization function is called once at the startup.
  *         This function has to be called before any other LED function can be called.
  *         It enables LED to be initialized with both required and optional parameters.
  * @param  void
  * @retval None
  */
int led_init(void)
{
    int32_t id;
    GPIO_InitTypeDef GPIO_InitStruct;

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

    for (id = 0; id < LED_NUM; id++)
    {
        GPIO_InitStruct.Pin = led_gpio[id].pin;
        LED_Set(id, LED_STATE_OFF); // Default: OFF
        HAL_GPIO_Init(led_gpio[id].port, &GPIO_InitStruct);

        led_timer[id].state = LED_STATE_OFF;
        led_timer[id].numBlinks = 0;
        led_timer[id].percent = 0;
        led_timer[id].percent_count = 0;
        led_timer[id].tmrName = LedTmrNames[id];

        led_timer[id].tmrId = xTimerCreateStatic(led_timer[id].tmrName, portMAX_DELAY, pdTRUE,
                              (void *)id, led_timer_cb, &led_timer[id].tmr);
    }

    // led_blink(LED_ALL, 3, 1, 2, 150);

    return 0;
}

/**
  * @brief  This function will set the given LEDs ON, OFF, BLINK, FLASH, or TOGGLE.
  *         If BLINK and FLASH mode are used, a set of default parameters will be used.
  *         To customize these parameters, HalLedBlink() has to be used.
  * @param  leds - bit mask of LEDs to be turned ON.
  *         - LED_1: LED1
  *         - LED_2: LED2
  *         - LED_3: LED3
  *         - LED_4: LED4
  * @param  mode - new mode for the LEDs:
  *         - LED_MODE_OFF: LED_MODE_OFF;
  *         - LED_MODE_ON: Turn ON the LED;
  *         - LED_MODE_BLINK: BLINK the LED;
  *         - LED_MODE_FLASH: FLASH the LED;
  * @retval None
  */
void led_set(uint32_t leds, uint32_t mode)
{
    int32_t id;

    for (id = 0; id < LED_NUM; id++)
    {
        if ((leds >> id) & 0x01)
        {
            switch (mode)
            {
            case LED_MODE_OFF:
                if (led_timer[id].numBlinks == 0)
                {
                    LED_Set(id, LED_STATE_OFF);
                }
                led_timer[id].state = LED_STATE_OFF;
                break;

            case LED_MODE_ON:
                if (led_timer[id].numBlinks == 0)
                {
                    LED_Set(id, LED_STATE_ON);
                }
                led_timer[id].state = LED_STATE_ON;
                break;
            }
        }
    }
}

/**
  * @brief  led_toggle
  * @param  leds - bit mask of LEDs to be turned ON.
  *         - LED_1: LED1
  *         - LED_2: LED2
  *         - LED_3: LED3
  *         - LED_4: LED4
  * @retval None
  */
void led_toggle(uint32_t leds)
{
    int32_t id;

    for (id = 0; id < LED_NUM; id++)
    {
        if (((leds >> id) & 0x01) && (led_timer[id].numBlinks == 0))
        {
            LED_Toggle(id);
        }
    }
}

/**
  * @brief  This function will blink the given LEDs based on the provided parameters.
  * @param  numBlinks - number of times the LED will blink
  * @param  percent - percentage of the cycle that is ON.
  * @param  period - time in milliseconds of one ON/OFF cycle
  * @retval None
  */
void led_blink(uint32_t leds, uint32_t numBlinks, uint32_t percent, uint32_t percent_max, uint32_t period)
{
    int32_t  id;

    for (id = 0; id < LED_NUM; id++)
    {
        if ((leds >> id) & 0x01)
        {
            led_timer[id].numBlinks = numBlinks;
            led_timer[id].percent = percent;
            led_timer[id].percent_max = percent_max;
            led_timer[id].percent_count = 0;

            xTimerStop(led_timer[id].tmrId, 0);
            xTimerChangePeriod(led_timer[id].tmrId, pdMS_TO_TICKS(period), 0);
            xTimerStart(led_timer[id].tmrId, 0);
        }
    }
}

/**
  * @brief  This function returns the current state of the LEDs.
  * @param  None
  * @retval 32-bit contains the current state of the LEDs. Each bit indicates the corresponding LED status.
  */
uint32_t led_get_state(void)
{
    int32_t id;
    uint32_t state = 0;

    for (id = 0; id < LED_NUM; id++)
    {
        state |= (LED_Get(id) & 0x01) << id;
    }

    return state;
}

/**
  * @}
  */

/******************* (C)COPYRIGHT 2018 *****END OF FILE***********************/
